#define _J_STRANICENJE_C_
#include "stranicenje.h"

#include "spremnik.h"
#include "postavke.h"
#include "razno.h"
#include "ispis.h"
#include "liste.h"
#include "procesi.h"
#include "prekidi.h"
#include <arh/kontekst.h>

//adresa prvog okvira (postaviti!)
static j_okvir_t *j_okvir;
static uint br_okvira;
//maska za brzo trazenje prvog slobodnog okvira
static uint32 *mj_okvir;

//"opis" spremnickog prostora procesa: sto je zauzeto, sto slobodno, ...
ZAUZMI ( j_segment_t, j_segment, ( NAJ_PROCESA * 10 ) );
//za svaki proces je opisano koje djelove spremnickog prostora koristi

/*
 * Inicijalizacija podsustava za stranicenje
 */
void j_inicijaliziraj_stranicenje ()
{
	//od '&pocetak_procesi_stranicenje' koristiti za stranicenje
	extern char pocetak_procesi_stranicenje, stranicenje_velicina;

	//na pocetak ostaviti mjesta za maske
	aint adresa = (aint) &pocetak_procesi_stranicenje;

	br_okvira = (uint) &stranicenje_velicina / VEL_STRANICE - 1;
	//"-1" -> prvi okvir za maske

	//poravnati na granicu 32 bita (zadnja dva bita adrese moraju biti 0)
	if ( adresa & 3 )
		adresa = ( adresa & 3 ) + 4;

	mj_okvir = (uint32 *) adresa;

	uint vel_maske = velicina_maske ( br_okvira );

	int i;
	for ( i = 0; i < vel_maske; i++ )
		mj_okvir[i] = 0; //sve zauzeto

	for ( i = 0; i < br_okvira; i++ )
		OZNACI_OKVIR_SLOBODAN ( i ); //slobodno

	adresa += vel_maske * sizeof (uint32);

	//poravnati na granicu stranice (zadnjih POMAK bitova adrese => 0)
	if ( adresa & ( VEL_STRANICE - 1 ) )
		adresa = ( adresa & ( ~( VEL_STRANICE - 1 ) ) ) + VEL_STRANICE;

	j_okvir = (j_okvir_t *) adresa;
}

/*
 * Pripremanje spremnickog prostora za novi proces
 */
void j_inicijaliziraj_stranice_za_proces ( int proces_id,
					   void *pocetna_adresa,
					   aint velicina,
					   bool u_jezgri, bool stranicenje )
{
	j_spremnik_t *spremnik = j_oppr_spremnik ( proces_id );

	int i;

	/*
	U "pravim" sustavima bi trebalo pri ucitavanju programa "parsirati"
	njegove segmente i prema tome ucitati odgovarajuce dijelove.
	U ovom je primjeru to ugradjeno u skriptu "ldscript.ld"
	*/

	//adrese (aps. i logicke)
	extern char programi_pocetak;
	//extern char pocetak_prve_stranice; = 0!
	extern char kraj_prve_stranice;
	extern char pravi_pocetak_programa;
	extern char kraj_prvog_dijela;
	extern char pocetak_mjesta_za_stog;
	//extern char programi_velicina;

	spremnik->stranicenje = ISTINA;
	spremnik->segmenti.prvi = NULL;
	spremnik->segmenti.zadnji = NULL;
	spremnik->stranice.zast = 0;
	spremnik->stranice.ind = 0;


	//ucitaj sve potrebno

	//0. stranica = od adrese 0 - privilegirano koristenje!
	//mora se napraviti! ne moze imati zastavicu 'SAMO_REZERVIRAJ'
	j_dodaj_procesu ( (void *) 0, (void *) 0, VEL_STRANICE, spremnik,
			  MAPIRAJ | U_PROCES | SAMO_ZA_CITANJE | ZA_JEZGRU );


	//ostale stranice procesa imaju zastavicu 'SAMO_REZERVIRAJ' da se
	//simulira "stranicenje na zahtjev"
	//efikasnije je ne staviti tu zastavicu!

	//1. stranica (i mozda jos par do nje - prema ldscript.ld!) - prazno
	//   kontekst dretvi; privilegirano koristenje,
	//   za pohranu i obnovu konteksta pri prihvatu prekida

	aint velicina_za_kontekste =
		(uint) &pravi_pocetak_programa - 
		(uint) &kraj_prve_stranice;

	j_dodaj_procesu ( NULL, (void *) VEL_STRANICE,
			  velicina_za_kontekste, spremnik,
			  PRAZNO | STVORI | U_PROCES | ZA_JEZGRU //);
			  | SAMO_REZERVIRAJ );

	spremnik->kontekst_dretvi = (void *) VEL_STRANICE;
	//logicka adresa!

	//inicijalizacija maske za kontekst (maske su u opisniku procesa)
	memset ( spremnik->mkontekst_dretvi, 0,
		 velicina_maske ( NAJ_DRETVI ) );

	for ( i = 0; i < NAJ_DRETVI; i++ )
	{
		aint pocetak = i * sizeof(arh_kontekst_t);
		aint kraj = pocetak + sizeof(arh_kontekst_t) - 1;

		//izracunaj adresu stranice
		pocetak &= ~( VEL_STRANICE - 1 );
		kraj &= ~( VEL_STRANICE - 1 );

		//koristiti kontekste koji nisu na prijelazima stranica
		if ( pocetak == kraj )
			POSTAVI_SLOBODNO ( spremnik->mkontekst_dretvi,
					   NAJ_DRETVI, i );
	}


	//ostatak sekcije ".programi"
	
	//instrukcije i konstante - .text, .rodata - mapirati
	//"pravi_pocetak_programa" -> "kraj_prvog_dijela"

	
	void *aps_adresa = &programi_pocetak +
			(uint) &pravi_pocetak_programa;
	aint velicina1 = ( (uint) &kraj_prvog_dijela ) -
			(uint) &pravi_pocetak_programa;

	j_dodaj_procesu ( aps_adresa, &pravi_pocetak_programa,
			  velicina1, spremnik,
			  MAPIRAJ | U_PROCES | ZA_PROCES | SAMO_ZA_CITANJE //);
			  | SAMO_REZERVIRAJ );


	//sekcije: .data; .bss
	//"kraj_prvog_dijela" -> "pocetak_mjesta_za_stog" - kopiraj

	aps_adresa = &programi_pocetak + (uint) &kraj_prvog_dijela;
	velicina1 = ( (uint) &pocetak_mjesta_za_stog ) -
			(uint) &kraj_prvog_dijela;

	j_dodaj_procesu ( aps_adresa, &kraj_prvog_dijela, velicina1, spremnik,
			  KOPIRAJ | STVORI | ZA_PROCES | U_PROCES //);
			  | SAMO_REZERVIRAJ );
	
	//na kraju mjesto za stog
	// "na kraju" moze biti odmah iza ili "skroz" iza!
	//neka bude "skroz" iza, zadnjih NAJ_DRETVI za stogove

	j_dodaj_procesu ( NULL , (void *) ( ( (aint) 0xffffffff) - 
			  NAJ_DRETVI * VEL_STRANICE + 1 ),
			  NAJ_DRETVI * VEL_STRANICE - 1, spremnik,
			  PRAZNO | STVORI | U_PROCES | ZA_PROCES //);
			  | SAMO_REZERVIRAJ );
			//umjesto 0xffffffff bi trebalo biti 0x100000000, ali
			//taj broj ne stane u 32 bita, pa je u izrazu:
			//0xffffffff - nesto + 1

	spremnik->stogovi = (void *) 0xfffffffc;
	//4 zadnja okteta se ne koriste, da se izbjegnu problemi s prelaskom
	//granice od 32 bita

	spremnik->segmentacija.cr0 = CR0_STRANICENJE;
	spremnik->segmentacija.cr3 = 
		(uint32) U_ADR ( spremnik->stranice.ind );

	//j_prikazi_tablicu ( &spremnik->stranice, 20 );
	//j_zaustavi();
}

/*
 * Dodaj stranice procesu
 * adr_u_jezgri:	sadrzaj sa kojim ih se puni (apsolutna adresa)
 * adr_u_proc:		logicka adresa pocetka segmenta (u procesu)
 * velicina:		velicina segmenta koji se dodaje
 * spremnik:		opisnik spremnika procesa
 * zast:		sto tocno napraviti je zadano zastavicama
 */
static void j_dodaj_procesu (	void *adr_u_jezgri, void *adr_u_proc,
				aint velicina, void *spremnik, uint zast )
{
	j_dodaj_u_adrese_procesa ( adr_u_proc, adr_u_jezgri, velicina,
				   spremnik, zast );

	if ( zast & SAMO_REZERVIRAJ )
		return;

	j_kopiraj_u_ili_iz_procesa (	adr_u_jezgri, adr_u_proc, velicina,
					spremnik, zast );
}

/*
 * Obrada prekida zbog promasaja stranice
 * Stvori trazenu stranicu, ako je u granicama rezerviranog prostora
 * Parametri se ignoriraju (id = 14, param=vrh korisnickog stoga dretve)
 */
void j_prekid_zbog_promasaja ( int id, void *param )
{
	j_spremnik_t *spremnik = j_oppr_spremnik ( j_aktivni_proces() );

	void *adresa = j_dohvati_adresu_promasaja ();

	//j_prikazi_translacije ( U_INT ( spremnik->stranice ), (aint) adresa );

	//mozda je na pomocnom spremniku?
	j_tabstr_t *stranica = j_dohvati_opisnik_stranice ( adresa, spremnik );

	if ( stranica != NULL && PROVJERI_ALOCIRANA ( stranica ) )
	{
		//dohvati ju iz pomocnog spremnika!
		PROVJERI ( NEISTINA, "Nije implementirano!\n" );

		return;
	}

	//dodaj/mapiraj/kopiraj/... stranicu
	j_sredi_stranicu ( adresa, spremnik );

	//j_prikazi_translacije ( U_INT ( spremnik->stranice ), (aint) adresa );
}

/*
 * Pretvaranje zadane relativne (logicke) u procesu u apsolutnu adresu u jezgri
 */
int j_adresa_u_jezgri (	void *adr_u_procesu, void **adr_u_jezgri,
			void *spremnik )
{
	j_tabstr_t *stranica = 
		j_dohvati_opisnik_stranice ( adr_u_procesu, spremnik );

	//ako nije prisutna, napravi/dohvati ju (ako treba)
	if ( stranica != NULL && PROVJERI_ALOCIRANA ( stranica ) &&
		!PROVJERI_PRISUTNOST ( stranica ) )
	{
		//dohvati ju iz pomocnog spremnika!
		PROVJERI ( NEISTINA, "Nije implementirano!\n" );
	}

	if ( stranica == NULL || !PROVJERI_ALOCIRANA ( stranica ))
	{
		//dohvati/ako je unutar nekog rezerviranog segmenta
		if ( !j_sredi_stranicu ( adr_u_procesu, spremnik ) )
			return -1;

		if ( stranica == NULL )
			stranica = j_dohvati_opisnik_stranice ( adr_u_procesu,
								spremnik );
	}

	j_adresa_t adr;
	adr.adr = (uint) adr_u_procesu;

	*adr_u_jezgri = U_ADR ( stranica->ind ) + adr.ind.p;

	return 0;
}

/*
 * Dohvaca/stvara/... trazenu stranicu, ako je rezervirana - ima je u opisu
 * adresnog prostora procesa
 */
static bool j_sredi_stranicu ( void *adresa, void *_spremnik )
{
	j_spremnik_t *spremnik = _spremnik;
	j_segment_t *segment = j_provjeri_adresu ( adresa, spremnik );

	if ( segment == NULL )
	{
		int id_dretve = j_id_dretve ( NULL );
		//aktivna dretva ?! (ne mora biti? - provjeri/popravi)

		j_ispisi ( "Greska u pristupu spremniku - dretva %d se ubija\n",
			   id_dretve );

		j_zavrsi_dretvu ( &id_dretve );

		return NEISTINA;
		//nekako drukcije se vratiti jer operacija koja se zapocela nije
		//uspjela!!! mozda kao pocetna dretva: "*pripremi*" i "*izadji*"
	}

	void *adresa_u_jezgri, *adresa_u_procesu;

	//je li stranica pocetak segmenta?
	if ( IZ_ADR ( segment->pocetak ) == IZ_ADR ( adresa ) )
	{
		//kopiraj dio stranice tj. od "segment->pocetak"
		adresa_u_procesu = segment->pocetak;
		adresa_u_jezgri = segment->ucitaj_od;
	}
	else {
		//kopiraj od pocetka stranice
		adresa_u_procesu = U_ADR ( IZ_ADR ( adresa ) );

		adresa_u_jezgri = segment->ucitaj_od + (aint) U_ADR ( 
			IZ_ADR ( adresa - (aint) segment->pocetak ) );
	}

	void *kraj;

	//je li stranica kraj segmenta?
	if ( IZ_ADR ( segment->kraj - 1 ) == IZ_ADR ( adresa ) )
	{
		kraj = segment->kraj;
	}
	else { //nije, kraj je pocetak iduce stranice
		kraj = U_ADR ( IZ_ADR ( adresa ) + 1 );
	}

	aint velicina = (aint) kraj - (aint) adresa_u_procesu;

	uint zast = segment->zast & ~SAMO_REZERVIRAJ;

	j_kopiraj_u_ili_iz_procesa ( adresa_u_jezgri, adresa_u_procesu,
				     velicina, spremnik, zast );

	//j_prikazi_translacije ( U_INT ( spremnik->stranice ), (uint) adresa );
	//j_prikazi_tablicu ( spremnik->stranice, 10 );

	return ISTINA;
}

/*
 * Puni dio procesa podacima
 * ili cita iz njega
 */
void j_kopiraj_u_ili_iz_procesa ( void *_adr_u_jezgri, void *_adr_u_proc,
					 aint velicina, void *_spremnik,
					 uint zast )
{
	j_spremnik_t *spremnik = _spremnik;

	j_adresa_t adr;
	aint za_kopirati = velicina;
	void *adr_u_jezgri = _adr_u_jezgri;
	void *adr_u_proc = _adr_u_proc;

	j_provjeri_i_dohvati ( &spremnik->stranice, ZA_TABLICU , NULL,
				NULL, spremnik );

	j_tabstr_t *r1 = U_ADR ( spremnik->stranice.ind );

	while ( za_kopirati > 0 )
	{
		adr.adr = (aint) adr_u_proc;

		j_tabstr_t *stranica = &r1[adr.ind.i1];

		#if IMA_IND_2 //postoji druga razina

		//ako stranica za tablicu ili podatke nije zauzeta, uzmi okvir
		j_provjeri_i_dohvati (	stranica, ZA_TABLICU,
					NULL, NULL, spremnik );

		j_tabstr_t *r2 = U_ADR ( stranica->ind );
		stranica = &r2[adr.ind.i2];

		#if IMA_IND_3 //postoji treca razina

		//ako stranica za tablicu ili podatke nije zauzeta, uzmi okvir
		j_provjeri_i_dohvati (	stranica, ZA_TABLICU,
					NULL, NULL, spremnik );

		j_tabstr_t *r3 = U_ADR ( stranica->ind );
		stranica = &r3[adr.ind.i3];

		#endif //IMA_IND_3
		#endif //IMA_IND_2

		j_kopiraj_u_ili_iz_stranice (	&adr_u_jezgri, &adr_u_proc,
						&za_kopirati, spremnik,
						stranica, zast );
	}
	//j_prikazi_tablicu ( spremnik->stranice, 10 );
}

/*
 * Puni dio stranice (ili cijelu) podacima
 * ili cita iz nje
 * Varijable 'adr_u_jezgri', 'adr_u_proc' i 'velicina' se nakon kopiranja
 * azuriraju novim vrijednostima.
 */
static void j_kopiraj_u_ili_iz_stranice ( void **adr_u_jezgri,
					  void **adr_u_proc,
					  aint *velicina, void *spremnik,
					  void *_stranica, uint zast )
{
	j_tabstr_t *stranica = _stranica;
	aint odmak = ( (aint) *adr_u_proc ) & ( VEL_STRANICE - 1 );

	void *adr_u_proc_aps;

	aint za_kopirati;

	j_provjeri_i_dohvati (	stranica, zast, *adr_u_proc, *adr_u_jezgri,
				spremnik );

	if ( odmak + *velicina <= VEL_STRANICE ) // odmak + *velicina < 2^32!
		za_kopirati = *velicina;
	else 
		za_kopirati = VEL_STRANICE - odmak;

	if ( !( zast & MAPIRAJ ) ) 
	{
		adr_u_proc_aps = U_ADR ( stranica->ind ) + odmak;

		if ( zast & KOPIRAJ )
		{
			PROVJERI ( ! ( adr_u_jezgri == NULL ),
				"Kriva adresa!\n" );

			if ( zast & U_JEZGRU )
				memcpy ( *adr_u_jezgri, adr_u_proc_aps,
					 za_kopirati );
			else
				memcpy ( adr_u_proc_aps, *adr_u_jezgri,
					 za_kopirati );
		}
	}

	//azuriraj do kuda je iskopirano
	if ( zast & ( KOPIRAJ | MAPIRAJ ) )
		*adr_u_jezgri += za_kopirati;

	*adr_u_proc += za_kopirati;

	*velicina -= za_kopirati;
}

/*
 * Provjeri da li stranica postoji
 * Ako ne, a treba, ako su zastavice povoljne uzmi novu
 * Ako se samo mapira prostor ubaci vrijednost u opis stranice
 */
static void j_provjeri_i_dohvati ( void *_stranica, uint zast, 
				   void *adr_u_proc, void *adr_u_jezgri,
				   void *spremnik )
{
	j_tabstr_t *stranica = _stranica;
	bool postoji = !PROVJERI_NIJE_NI_REZERVIRANA ( stranica );

	if ( zast & ZA_TABLICU )
	{
		if ( PROVJERI_PRISUTNOST ( stranica ) )
			return;

		if ( postoji )
		{
			//na pomocnom je spremniku. dohvati ju
			PROVJERI ( NEISTINA, "Nije implementirano!\n" );

			return;
		}
		
		*stranica = j_uzmi_prazni_okvir ();

		OZNACI_CITAJ_PISI ( stranica );
		OZNACI_ZA_DRETVU ( stranica );
		//mora biti oznacena 'za dretvu' iako pokazuje na iducu tablicu
		//a ne stranicu

		return;
	}

	if ( zast & MAPIRAJ )
	{
		if ( postoji )
		{
			//obrisi okvir
			if ( PROVJERI_PRISUTNOST ( stranica ) )
			{
				j_oslobodi_stranicu ( stranica );
			}
			else {
				//stranica je na pomocnom spremniku, makni ju
				PROVJERI ( NEISTINA, "Nije implementirano!\n" );
			}
		}

		stranica->ind = IZ_ADR ( adr_u_jezgri );
	}
	else {
		if ( postoji && !PROVJERI_PRISUTNOST ( stranica ) )
		{
			//dohvati je iz pomocnog spremnika
			PROVJERI ( NEISTINA, "Nije implementirano!\n" );
		}

		if ( !postoji && ( zast & STVORI ||
			j_provjeri_adresu ( adr_u_proc, spremnik ) != NULL ) )
		{
			//zauzmi novu
			*stranica = j_uzmi_prazni_okvir ();

			postoji = ISTINA;
		}

		PROVJERI ( postoji, "Stranica ne postoji!\n" );
	}

	OZNACI_PRISUTNA ( stranica );
	OZNACI_ALOCIRANA ( stranica );

	if ( zast & SAMO_ZA_CITANJE )
		OZNACI_CITAJ ( stranica );
	else
		OZNACI_CITAJ_PISI ( stranica );

	if ( zast & ZA_JEZGRU )
		OZNACI_ZA_JEZGRU ( stranica );
	else
		OZNACI_ZA_DRETVU ( stranica );
}

/*
 * Dohvati opisnik stranice koja sadrzi zadanu adresu
 * NULL, ako ta stranica nije "opisana" u tablicama prevodjenja (ne provjerava
 * se je li rezervirana!)
 */
static void *j_dohvati_opisnik_stranice ( void *adresa, void *_spremnik )
{
	j_spremnik_t *spremnik = _spremnik;
	j_adresa_t adr;

	adr.adr = (uint) adresa;

	j_tabstr_t *stranica = &spremnik->stranice;

	if ( !PROVJERI_ALOCIRANA ( stranica ) )
		return NULL;
	j_provjeri_i_dohvati ( stranica, ZA_TABLICU, NULL, NULL, spremnik );

	j_tabstr_t *r1 = U_ADR ( stranica->ind );
	stranica = &r1[adr.ind.i1];

	#if IMA_IND_2

	if ( !PROVJERI_ALOCIRANA ( stranica ) )
		return NULL;
	j_provjeri_i_dohvati ( stranica, ZA_TABLICU, NULL, NULL, spremnik );

	j_tabstr_t *r2 = U_ADR ( stranica->ind );
	stranica = &r2[adr.ind.i2];

	#if IMA_IND_3

	if ( !PROVJERI_ALOCIRANA ( stranica ) )
		return NULL;
	j_provjeri_i_dohvati ( stranica, ZA_TABLICU, NULL, NULL, spremnik );

	j_tabstr_t *r3 = U_ADR ( stranica->ind );
	stranica = &r3[adr.ind.i3];

	#endif	//IMA_IND_3
	#endif	//IMA_IND_2

	return stranica;
}

/*
 * Dohvati prazni okvir i pocetno ga popuni nulama
 */
static j_tabstr_t j_uzmi_prazni_okvir ()
{
	int str = DOHVATI_PRVI_SLOBODAN_OKVIR ();

	if ( str == -1 ) {
		//sad bi tu trebalo oslobodit neki okvir (popraviti)!
		PROVJERI ( 0, "Nema slobodna okvira!\n" );
	}

	memset ( &j_okvir[str], 0, VEL_STRANICE );

	j_tabstr_t stranica;

	stranica.ind = IZ_ADR ( &j_okvir[str] );
	stranica.zast = STR_PRISUTNA | STR_ALOCIRANA;

	return stranica;
}

/*
 * Rad s "mapom" spremnika procesa - dodavanje segmenta u mapu
 */
static void j_dodaj_u_adrese_procesa ( void *adr_u_proc, void *adr_u_jezgri,
			        aint velicina, void *_spremnik, uint zast )
{
	j_spremnik_t *spremnik = _spremnik;
	j_segment_t *segment;
	int id;

	id = DOHVATI ( j_segment );

	segment = &j_segment[id];

	segment->pocetak = adr_u_proc;
	segment->kraj = adr_u_proc + velicina;
	segment->ucitaj_od = adr_u_jezgri;
	segment->zast = zast;
	segment->iduci = NULL;

	DODAJ_U_SORT_LISTU ( &spremnik->segmenti, segment, USPOREDI_SEGMENTE );
	//pretpostavka je da se segmenti ne preklapaju!

	//poboljsanje: spajanje susjednih segmenata istih zastavica
	//poboljsanje: rad sa segmentima koji se preklapaju
}

/*
 * Provjeri je li zadana adresa u "rezerviranom" podrucju procesa
 */
static void *j_provjeri_adresu ( void *adr_u_proc, void *_spremnik )
{
	j_spremnik_t *spremnik = _spremnik;
	j_lista_segmenata_t *segmenti = &spremnik->segmenti;

	j_segment_t *segment;

	//pronadnji je li zadana adresa u nekom segmentu
	segment = POGLEDAJ_PRVI_ELEMENT ( segmenti );
	while ( segment != NULL )
	{
		if ( segment->pocetak <= adr_u_proc &&
			adr_u_proc < segment->kraj )
			return segment;

		segment = POGLEDAJ_IDUCI_ELEMENT ( segment );
	}

	return NULL;
}

/*
 * Ispis tablice prevodjenja (samo zadani broj elemenata, pocevsi od pocetka)
 */
void j_prikazi_tablicu ( void *_pocetak, int br_zapisa )
{
	j_tabstr_t *pocetak = _pocetak;
	int brojac = 0;

	j_ispisi ( "Pocetna tablica nalazi se na = %x\n", *pocetak );

	j_tabstr_t *r1 = U_ADR (pocetak->ind);

	int i;
	for ( i = 0; i < 1 << VEL_IND_1 && brojac < br_zapisa; i++ )
	{
		if ( U_INT ( r1[i] ) == 0 )
			continue;

		j_ispisi ( "r1[%d]: %x\n", i, U_INT ( r1[i] ) );
		brojac++;

		if ( !PROVJERI_PRISUTNOST ( &r1[i] ) )
			continue;

		#if IMA_IND_2

		j_tabstr_t *r2 = U_ADR ( r1[i].ind );
		int j;
		for ( j = 0; j < 1 << VEL_IND_2 && brojac < br_zapisa; j++ )
		{
			if ( U_INT ( r2[j] ) == 0 )
				continue;

			j_ispisi ( "\tr2[%d]: %x\n", j, U_INT ( r2[j] ) );
			brojac++;

			if ( !PROVJERI_PRISUTNOST ( &r2[j] ) )
				continue;

			#if IMA_IND_3

			j_tabstr_t *r3 = U_ADR ( r2[j].ind );
			int k;
			for ( k = 0; k < 1 << VEL_IND_3 && brojac < br_zapisa;
				k++ )
			{
				if ( U_INT ( r3[k] ) == 0 )
					continue;

				j_ispisi ( "\t\tr3[%d]: %x\n", k,
						U_INT ( r3[k] ) );
				brojac++;
			}
			#endif //ima_IND_3
		}
		#endif //ima_IND_2
	}

	j_ispisi ( "\n" );
}

/*
 * Ispis koje se tablice koriste pri translaciji adresa
 */
void j_prikazi_translacije ( aint pocetak, aint adresa )
{
	aint maska = ~( VEL_STRANICE - 1 ), stranica;
	j_adresa_t adr;

	adr.adr = adresa;

	j_ispisi ( "adresa=%x -> ", adresa );

	stranica = pocetak;

	aint *r1 = (aint *) ( stranica & maska );

	j_ispisi ( "r1=%x [%d] -> ", stranica, adr.ind.i1 );

	stranica = r1[adr.ind.i1];

	#if IMA_IND_2

	aint *r2 = (aint *) ( stranica & maska );

	j_ispisi ( "r2=%x [%d] -> ", stranica, adr.ind.i2 );

	stranica = r2[adr.ind.i2];

	#if IMA_IND_3

	aint *r3 = (aint *) ( stranica & maska );

	j_ispisi ( "r3=%x [%s] -> ", stranica, adr.ind.i3 );

	stranica = r3[adr.ind.i3];

	#endif //IMA_IND_3
	#endif //IMA_IND_2

	j_ispisi ( "%x\n", stranica );
}


// Daljnji kod nije ispitan !!!

/*
 * Oslobadjanje stranice i okvira koji je ona koristila
 */
static void j_oslobodi_stranicu ( void *_stranica )
{
	j_tabstr_t *stranica = _stranica;

	if ( stranica->zast != 0 )
	{
		//provjeri da nije na pomocnom spremniku
		if ( !PROVJERI_PRISUTNOST ( stranica ) &&
		      PROVJERI_ALOCIRANA ( stranica ) )
		{
			//oznaci da je ta stranica slobodna na pomocnom spr.
			PROVJERI ( NEISTINA, "Nije implementirano!\n" );
		}

		int str = ( ( stranica->ind << POMAK ) - (uint ) &j_okvir[0] )
				/ VEL_STRANICE;

		//provjeri da li pokazuje na okvir iz skupa okvira!
		//ako ne onda ne brisi
		if ( str >= 0 && str < br_okvira )
			OZNACI_OKVIR_SLOBODAN ( str );
	}

	//oznaci "stranica ne postoji"
	stranica->ind = 0;
	stranica->zast = 0;
}

/*
 * Oslobodi spremnicki prostor procesa - sve segmente koje koristi
 */
void j_oslobodi_spremnicki_prostor ( void *_spremnik )
{
	j_spremnik_t *spremnik = _spremnik;
	j_lista_segmenata_t *segmenti = &spremnik->segmenti;
	j_segment_t *segment;

	//oslobadjaj segmente po redu
	segment = POGLEDAJ_PRVI_ELEMENT ( segmenti );

	while ( segment != NULL )
	{
		j_makni_iz_procesa ( segment->pocetak,
				(aint) segment->kraj - (aint) segment->pocetak,
				spremnik );

		segment = POGLEDAJ_PRVI_ELEMENT ( segmenti );
	}
}

/*
 * Makni segment iz opisa procesa i oslobodi stranice koje taj dio predstavljaju
 */
void j_makni_iz_procesa ( void *adr_u_proc, aint velicina, void *_spremnik )
{
	j_spremnik_t *spremnik = _spremnik;
	j_adresa_t adr_od, adr_do;
	uint zast;

	zast = j_makni_iz_adresa_procesa ( adr_u_proc, velicina, spremnik );

	if ( zast == 0 )
		return; //segment nije ni rezerviran

	PROVJERI ( PROVJERI_ALOCIRANA ( &spremnik->stranice ),
		   "Stranicenje nije ni bilo zapocet\n" );

	if ( !PROVJERI_PRISUTNOST ( &spremnik->stranice ) )
	{
		//stranica nije u radnom spremniku, najprije ju treba dohvatiti
		PROVJERI ( NEISTINA, "Stranicenje nije u spremniku - "
			   "dohvat nije implementiran.\n" );

		return;
	}

	//sto ako adresa nije pocetak stranice?
	// - trebalo bi ispitati da li se dio prije koristi,
	//   tj. je li rezerviran, ako je ostavi tu stranicu, ako nije obrisi ju
	// - isti problem i sa krajem, ako se ne poklapa s pocetkom iduce

	//trenutno rjesenje to ne provjerava - oslobadja sve stranice koje
	//sadrze adrese koje treba osloboditi

	adr_od.adr = ( (uint) adr_u_proc ) & ~( VEL_STRANICE - 1 );
	adr_do.adr = ( (uint) adr_u_proc + velicina - 1) & ~( VEL_STRANICE - 1 );

	j_tabstr_t *r1 = U_ADR ( spremnik->stranice.ind );

	while ( adr_od.adr <= adr_do.adr )
	{
		j_tabstr_t *stranica = &r1[adr_od.ind.i1];

		#if IMA_IND_2 //postoji druga razina

		if ( PROVJERI_NIJE_NI_REZERVIRANA ( stranica ) )
		{
			//cijelo "podstablo" je prazno

			//pazi da ne ide preko dozvoljenog prostora
			if ( adr_od.adr > ( (aint) -1 ) - 
				( 1 << ( IMA_IND_2 + IMA_IND_3 + POMAK ) ) )
				break;
				
			adr_od.adr += 1 << ( IMA_IND_2 + IMA_IND_3 + POMAK );
			continue;
		}
		else if ( !PROVJERI_PRISUTNOST ( stranica ) )
		{
			//dohvati s pomocnog spremnika
			PROVJERI ( NEISTINA, "Nije implementirano (%x)!\n", 
				*stranica );
		}

		j_tabstr_t *r2 = U_ADR ( stranica->ind );
		stranica = &r2[adr_od.ind.i2];

		#if IMA_IND_3 //postoji treca razina

		//ako stranica za tablicu ili podatke nije zauzeta, uzmi okvir

		if ( PROVJERI_NIJE_NI_REZERVIRANA ( stranica ) )
		{
			//cijelo "podstablo" je prazno
			//pazi da ne ide preko dozvoljenog prostora
			if ( adr_od.adr > ( (aint) -1 ) - 
				( 1 << ( IMA_IND_3 + POMAK ) ) )
				break;

			adr_od.adr += 1 << ( IMA_IND_3 + POMAK );
			continue;
		}
		else if ( !PROVJERI_PRISUTNOST ( stranica ) )
		{
			//dohvati s pomocnog spremnika
			PROVJERI ( NEISTINA, "Nije implementirano (%x)!\n", 
				*stranica );
		}

		j_tabstr_t *r3 = U_ADR ( stranica->ind );
		stranica = &r3[adr_od.ind.i3];

		#endif //IMA_IND_3
		#endif //IMA_IND_2

		if ( zast & MAPIRAJ )
		{
			//samo obrisi zapis u tablici
			stranica->ind = 0;
			stranica->zast = 0;
		}
		else {
			j_oslobodi_stranicu ( stranica );
		}

		//da li je sad cijela tablica prazna?
		#if IMA_IND_2 //postoji druga razina
		#if IMA_IND_3 //postoji treca razina

		//je li r3 prazan?
		if ( memtestzero_uint ( r3, VEL_STRANICE ) )
			j_oslobodi_stranicu ( &r2[adr_od.ind.i2] );

		#endif //IMA_IND_3

		//je li r2 prazan?
		if ( memtestzero_uint ( r2, VEL_STRANICE ) )
			j_oslobodi_stranicu ( &r1[adr_od.ind.i1] );

		#endif //IMA_IND_2

		//je li r1 prazan?
		if ( memtestzero_uint ( r1, VEL_STRANICE ) )
		{
			j_oslobodi_stranicu ( &spremnik->stranice );
			break; //sve je slobodno, nije potrebno dalje ici
		}

		if ( adr_od.adr > ( (aint) -1 ) - VEL_STRANICE )
				break;

		adr_od.adr += VEL_STRANICE;
	}
}

/*
 * Makni segment iz opisa procesa
 */
static uint j_makni_iz_adresa_procesa ( void *adr_u_proc, aint velicina,
					void *_spremnik )
{
	j_spremnik_t *spremnik = _spremnik;
	j_lista_segmenata_t *segmenti = &spremnik->segmenti;
	j_segment_t *segment;
	uint zast;

	//pronadnji koji je to segment
	segment = POGLEDAJ_PRVI_ELEMENT ( segmenti );
	while ( segment != NULL && segment->pocetak < adr_u_proc )
		segment = POGLEDAJ_IDUCI_ELEMENT ( segment );

	if ( segment == NULL || segment->pocetak != adr_u_proc )
	{
		j_ispisi ( "Nema segmenta u adresama procesa!\n" );
		zast = 0;
	}
	else {
		MAKNI_IZ_LISTE ( segmenti, segment );

		OBRISI ( j_segment, ( segment - j_segment ) );

		zast = segment->zast;
	}

	return zast;

	//Poboljsanje: 'pocetak' se ne mora poklapati s nekim postojecim
	//segmentom! Omoguci dijeljenje tako da se 'zadani' segment otpusti
}
